#include "tst_lightControlEngineTest.h"

#include "qtest.h"
#include "qsignalspy.h"

#include "LightPresetData.h"
#include "LightPresetModel.h"
#include "LightEngine.h"
#include "LightPresetFactory.h"
#include "ActionListController.h"
#include "mock_lighttransitionengine.h"
#include "dmxinterface.h"
#include "mock_dmxInterface.h"
#include "modelViewRules.h"

#define  INVALID_CHANNEL      0xFFFF
#define  NUM_OF_CHANNELS_FOR_TEST   3  /**< spy dmx interface has few channel, just for simplicity  */

LightControlEngineTest::LightControlEngineTest(QObject *parent) :
   QObject(parent)
{
   qRegisterMetaType< QList<double> >("QList<double>");
}

/** executed before every test case */
void LightControlEngineTest::init()
{
   LightPresetFactory *presetFactory = new LightPresetFactory();
   m_presetModel = new LightPresetModel( NUM_OF_CHANNELS_FOR_TEST, presetFactory, this);
   m_mockTransitionEngine = new MockLightTransitionEngine( this);
   m_controller = new ActionListController( *m_presetModel, this);
   m_lightEngine = new LightEngine( *m_presetModel, *m_mockTransitionEngine, *m_controller, this);

   SpyDmxInterface::reset(NUM_OF_CHANNELS_FOR_TEST);
}

/** executed after every test case */
void LightControlEngineTest::cleanup()
{
   delete m_lightEngine;
   delete m_mockTransitionEngine;
   delete m_presetModel;
}

void LightControlEngineTest::creation()
{
   QVERIFY( m_lightEngine != NULL);
   QCOMPARE( SpyDmxInterface::getValueAtChannel(0), 0.);
   QCOMPARE( SpyDmxInterface::getValueAtChannel(NUM_OF_CHANNELS_FOR_TEST -1), 0.);
}

void LightControlEngineTest::createNewPreset()
{
   QSignalSpy sig_ModelLayoutChanged( m_presetModel, SIGNAL(layoutChanged()) );

   m_presetModel->updatePreset( QString("My preset"), 1000,
                                QList<double>() << 10. << 20. << 30.);

   QCOMPARE( sig_ModelLayoutChanged.length(), 1);
   QCOMPARE( m_presetModel->rowCount(), 1);
}

void LightControlEngineTest::updateExistingPreset()
{
   QSignalSpy sig_layoutChanged( m_presetModel, SIGNAL(layoutChanged()) );

   m_presetModel->updatePreset( QString("My preset"), 1000,
                                QList<double>() << 10. << 20. << 30.);
   m_presetModel->updatePreset( QString("My preset"), 1500,
                                QList<double>() << 50. << 60.);

   QCOMPARE( sig_layoutChanged.length(), 2);
   QCOMPARE( m_presetModel->rowCount(), 1);

   const LightPresetData *readPreset;
   readPreset = m_presetModel->index( 0).data( modelViewRules::Binary).value<const LightPresetData *>();
   QVERIFY(readPreset != NULL);

   QCOMPARE( readPreset->fadeTime(), 1500);
   /* level list is completely replaced. Missing value is set to 0 */
   QCOMPARE( readPreset->levelList().length(), 3);
   QCOMPARE( readPreset->levelList().at(2), 0.0);
}

void LightControlEngineTest::startDmxService()
{
   QSignalSpy sig_dmxStarted( m_lightEngine, SIGNAL(dmxStarted()) );

   /* 'requestDmxStart' will succeed (and start signal is emitted)*/
   SpyDmxInterface::setDmxStartResult(true);

   m_lightEngine->requestDmxStart();
   QCOMPARE( sig_dmxStarted.length(), 1);
   QCOMPARE(DmxInterface::isDmxServiceStarted(), true);
}

void LightControlEngineTest::startDmxServiceFailure()
{
   QSignalSpy sig_dmxStarted( m_lightEngine, SIGNAL(dmxStarted()) );

   /* 'requestDmxStart' will fail (and no signal is emitted)*/
   SpyDmxInterface::setDmxStartResult(false);

   m_lightEngine->requestDmxStart();
   QCOMPARE( sig_dmxStarted.length(), 0);
   QCOMPARE(DmxInterface::isDmxServiceStarted(), false);
}

void LightControlEngineTest::stopDmxService()
{
   QSignalSpy sig_dmxStopped( m_lightEngine, SIGNAL(dmxStopped()) );

   SpyDmxInterface::setDmxStartResult(true);
   m_lightEngine->requestDmxStart();
   QCOMPARE(DmxInterface::isDmxServiceStarted(), true);

   m_lightEngine->requestDmxStop();
   QCOMPARE( sig_dmxStopped.length(), 1);
   QCOMPARE(DmxInterface::isDmxServiceStarted(), false);
}

void LightControlEngineTest::activateByLabel()
{
   m_presetModel->updatePreset( QString("My preset"), 1000,
                                QList<double>() << 10. << 20. << 30.);

   m_lightEngine->triggerTransition( QString("My preset") );
   QCOMPARE( m_mockTransitionEngine->lastFadeTime(), 1000 );
   QCOMPARE( m_mockTransitionEngine->lastRequestedValues()->length(), 3 );
   QCOMPARE( m_mockTransitionEngine->lastRequestedValues()->at(0), 10. );
}

void LightControlEngineTest::activateUnexistingLabel()
{
   QString exceptionLabel = QString();

   try
   {
      m_lightEngine->triggerTransition( QString("Unexisting") );
   }
   catch (QString &msg)
   {
      exceptionLabel = msg;
   }

   QCOMPARE( m_mockTransitionEngine->lastFadeTime(), 0 );
   QVERIFY( m_mockTransitionEngine->lastRequestedValues() == NULL );
   QCOMPARE( exceptionLabel, QString("No preset has label: Unexisting"));
}

void LightControlEngineTest::transitionNotification()
{
   QSignalSpy  sig_transitionFadestep( m_mockTransitionEngine,
                                       SIGNAL(fadeStep(QList<double>)) );

   m_presetModel->updatePreset( QString("My preset"), 1000,
                                QList<double>() << 10. << 20. << 30.);

   m_lightEngine->triggerTransition( QString("My preset") );

   m_mockTransitionEngine->simulateTick();  /* now one tick completes the transition */

   /* on tick, new values must be notified ... */
   QCOMPARE(sig_transitionFadestep.length(), 1);
   /* ... and appllied to DMX interface*/
   QCOMPARE(SpyDmxInterface::getValueAtChannel(0), 10.);
   QCOMPARE(SpyDmxInterface::getValueAtChannel(1), 20.);
   QCOMPARE(SpyDmxInterface::getValueAtChannel(2), 30.);
}

void LightControlEngineTest::deleteExistingPreset()
{
   QSignalSpy sig_layoutChanged( m_presetModel, SIGNAL(layoutChanged()) );

   m_presetModel->updatePreset( QString("My preset"), 1000,
                                QList<double>() << 10. << 20. << 30.);

   m_presetModel->removePreset( QString("My preset") );

   QCOMPARE(sig_layoutChanged.length(), 2);
   QCOMPARE(m_presetModel->rowCount(), 0);
}

void LightControlEngineTest::deleteInvalidPreset()
{
   QSignalSpy sig_layoutChanged( m_presetModel, SIGNAL(layoutChanged()) );

   m_presetModel->removePreset( QString("Unexisting") );

   /* nothing is done */
   QCOMPARE( m_presetModel->rowCount(), 0 );
   QCOMPARE( sig_layoutChanged.length(), 0);
}

void LightControlEngineTest::setValueFromGUI()
{
   /* set 20% at channel 0 */
   m_lightEngine->setLevelForChannel( 20., 0);

   /* transition engine and DMX interface must receive notification */
   QCOMPARE(m_mockTransitionEngine->spyLevelAtChannel(0), 20.);
   QCOMPARE(SpyDmxInterface::getValueAtChannel(0), 20.);
}

void LightControlEngineTest::accessInvalidChannel()
{
   try
   {
      m_lightEngine->setLevelForChannel( 20., INVALID_CHANNEL);
   }
   catch(...)
   {
      QFAIL("No exception must be generated");
   }
}

void LightControlEngineTest::retrieveDataForOne()
{
   m_presetModel->updatePreset( QString("My preset"), 1000,
                                QList<double>() << 10. << 20. << 30.);

   const LightPresetData *preset;
   QModelIndex firstIndex = m_presetModel->index( 0 );
   preset = m_presetModel->data( firstIndex, modelViewRules::Binary).value<const LightPresetData *>();
   QVERIFY( preset != NULL);

   QCOMPARE( preset->name(), QString("My preset"));
   QCOMPARE( preset->fadeTime(), 1000);
}

void LightControlEngineTest::retrieveAllData()
{
   QList<LightPresetData> somePresets = \
      QList<LightPresetData>() <<
      LightPresetData( QString("first preset"), 1000, QList<double>() << 10. << 20. << 30. ) <<
      LightPresetData( QString("second preset"), 2000, QList<double>() << 11. << 21. << 31. ) <<
      LightPresetData( QString("third preset"), 3000, QList<double>() << 12. << 22. << 32. );

   for (int i = 0; i < somePresets.length(); i++)
   {
      m_presetModel->updatePreset( somePresets.at(i) );
   }

   int numberOfPresets = m_presetModel->rowCount();
   QVERIFY( numberOfPresets == 3 );

   for (int i = 0; i < numberOfPresets; i++)
   {
      QModelIndex index = m_presetModel->index(i);
      QVERIFY( index != QModelIndex() );

      const LightPresetData *lightData;
      lightData = m_presetModel->data( index, modelViewRules::Binary).value<const LightPresetData *>();
      QVERIFY( lightData!= NULL);

      QCOMPARE( lightData->name(), somePresets.at(i).name() );
   }
}

